/**
 * 数据库数据管理模块
 * 针对数据库中的表设计一个类，通过类实例化的对象管理对应的表数据
 */

#pragma once
#include"Detail.hpp"
#include<mutex>
#include<cassert>

#define INSERT_USER "insert user values(null,'%s',sha2('%s',256),1000,0,0);"
#define LOGIN_USER "select id,score,total_count,win_count from user where username='%s' and password=sha2('%s',256);"
#define SELECT_BY_NAME "select id,score,total_count,win_count from user where username='%s';"
#define SELECT_BY_ID "select username,score,total_count,win_count from user where id=%ld;"
#define CHANGE_WIN "update user set score=score+20,total_count=total_count+1,win_count=win_count+1 where id=%ld;"
#define CHANGE_LOSE "update user set score=score-20,total_count=total_count+1 where id=%ld;"

namespace OnlineGoBang
{
    //用户数据表类
    class UserTable
    {
    public:
        UserTable( const std::string &host,
                    const std::string &username,
                    const std::string &password,
                    const std::string &dbname,
                    uint16_t port = 3306)
        {
            _mysql=Detail::MySql::MySql_Create(host,username,password,dbname,port);
            assert(_mysql!=NULL);
        }
        ~UserTable()
        {
            Detail::MySql::MySql_Destroy(_mysql);
            _mysql=nullptr;
        }

        //注册新用户
        bool Insert(const Json::Value& user)
        {
            if(user["username"].isNull()||user["password"].isNull())
            {
                LOG(DEBUG,"用户信息不完整\n");
                return false;
            }
            Json::Value val;
            bool ret=Select_by_Name(user["username"].asCString(),val);
            if(ret)//用户已存在，插入失败
            {
                LOG(DEBUG,"新增的用户已存在\n",user["username"].asCString());
                return false;
            }
            char sql[4096]={0};
            sprintf(sql,INSERT_USER,user["username"].asCString(),user["password"].asCString());
            ret = Detail::MySql::MySql_Exec(_mysql,sql);
            //LOG(DEBUG,"sql语句执行完成\n");
            if(!ret)
            {
                LOG(DEBUG,"新增用户出错\n");
                return false;
            }
            LOG(DEBUG,"新增用户成功\n");
            return true;
        }

        //用户登录验证
        bool Login(Json::Value& user)
        {
            if(user["username"].isNull()||user["password"].isNull())
            {
                LOG(DEBUG,"用户信息不完整\n");
                return false;
            }
            char sql[4096]={0};
            sprintf(sql,LOGIN_USER,user["username"].asCString(),user["password"].asCString());
            MYSQL_RES* res=nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex);

                bool ret=Detail::MySql::MySql_Exec(_mysql,sql);
                if(!ret)
                {
                    LOG(DEBUG,"用户登录验证失败\n");
                    return false;
                }
                //获取查询语句的执行结果
                res=mysql_store_result(_mysql);
                if(res==NULL)
                {
                    LOG(DEBUG,"查询结果获取失败\n");
                    return false;
                }
            }
            int row_num=mysql_num_rows(res);//获取查询结果的行数
            if(row_num==0)
            {
                LOG(DEBUG,"查询的用户不存在\n");
                mysql_free_result(res);
                return false;
            }
            if(row_num!=1)
            {
                LOG(DEBUG,"查询的用户不唯一\n");
                mysql_free_result(res);
                return false;
            }
            //获取一行查询出来的信息
            MYSQL_ROW row=mysql_fetch_row(res);
            user["id"]=(Json::UInt64)std::stol(row[0]);
            user["score"]=(Json::UInt64)std::stol(row[1]);
            user["total_count"]=std::stoi(row[2]);
            user["win_count"]=std::stoi(row[3]);

            mysql_free_result(res);
            return true;
        }

        //通过名字获取用户信息
        bool Select_by_Name(const std::string& name,Json::Value& user)
        {
            char sql[4096]={0};
            sprintf(sql,SELECT_BY_NAME,name.c_str());
            MYSQL_RES* res=nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex);

                bool ret=Detail::MySql::MySql_Exec(_mysql,sql);
                if(!ret)
                {
                    LOG(DEBUG,"通过用户名获取用户信息失败\n");
                    return false;
                }
                //获取查询语句的执行结果
                res=mysql_store_result(_mysql);
                if(res==NULL)
                {
                    LOG(DEBUG,"用户%s查询结果获取失败\n",name.c_str());
                    return false;
                }
            }

            int row_num=mysql_num_rows(res);//获取查询结果的行数
            if(row_num==0)
            {
                LOG(DEBUG,"查询的用户不存在\n");
                mysql_free_result(res);
                return false;
            }
            if(row_num!=1)
            {
                LOG(DEBUG,"查询的用户不唯一\n");
                mysql_free_result(res);
                return false;
            }
            //获取一行查询出来的信息
            MYSQL_ROW row=mysql_fetch_row(res);
            user["username"]=name;
            user["id"]=(Json::UInt64)std::stol(row[0]);
            user["score"]=(Json::UInt64)std::stol(row[1]);
            user["total_count"]=std::stoi(row[2]);
            user["win_count"]=std::stoi(row[3]);

            mysql_free_result(res);
            return true;
        }

        //通过id获取用户信息
        bool Select_by_ID(const uint64_t& id,Json::Value& user)
        {
            char sql[4096]={0};
            sprintf(sql,SELECT_BY_ID,id);
            MYSQL_RES* res=nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex);

                bool ret=Detail::MySql::MySql_Exec(_mysql,sql);
                if(!ret)
                {
                    LOG(DEBUG,"通过用户ID获取用户信息失败\n");
                    return false;
                }
                //获取查询语句的执行结果
                res=mysql_store_result(_mysql);
                if(res==NULL)
                {
                    LOG(DEBUG,"用户%ld查询结果获取失败\n",id);
                    return false;
                }
            }

            int row_num=mysql_num_rows(res);//获取查询结果的行数
            if(row_num==0)
            {
                LOG(DEBUG,"查询的用户不存在\n");
                mysql_free_result(res);
                return false;
            }
            if(row_num!=1)
            {
                LOG(DEBUG,"查询的用户不唯一\n");
                mysql_free_result(res);
                return false;
            }
            //获取一行查询出来的信息
            MYSQL_ROW row=mysql_fetch_row(res);
            user["username"]=row[0];
            user["id"]=(Json::UInt64)id;
            user["score"]=(Json::UInt64)std::stol(row[1]);
            user["total_count"]=std::stoi(row[2]);
            user["win_count"]=std::stoi(row[3]);

            mysql_free_result(res);
            return true;
        }

        //用户胜利时，当前分数增加，战斗场次增加，胜场增加
        bool Win(const uint64_t& id)
        {
            char sql[4096]={0};
            sprintf(sql,CHANGE_WIN,id);
            bool ret=Detail::MySql::MySql_Exec(_mysql,sql);
            if(!ret)
            {
                LOG(DEBUG,"胜利方数据更新出错\n");
                return false;
            }
            return true;
        }

        //用户失败时，当前分数减少，战斗场次增加
        bool Lose(const uint64_t& id)
        {
            char sql[4096]={0};
            sprintf(sql,CHANGE_LOSE,id);
            bool ret=Detail::MySql::MySql_Exec(_mysql,sql);
            if(!ret)
            {
                LOG(DEBUG,"失败方数据更新出错\n");
                return false;
            }
            return true;
        }

    private:
        MYSQL* _mysql;//MySql的操作句柄
        std::mutex _mutex;//MySql单独操作是线程安全的，但是当多个操作混合使用时，无法保证线程安全，需要加锁
    };
}